package org.springframework.boot.autoconfigure.data.redis;

import cn.ewan.cloud.common.support.Log;
import cn.ewan.cloud.common.web.support.BusinessException;
import cn.ewan.cloud.common.web.support.redis.spring.CommonRedisAutoConfiguration;
import cn.ewan.cloud.tenant.data.router.core.business.config.tenant.TenantUser;
import cn.ewan.cloud.tenant.data.router.core.common.exception.TenantMsgCode;
import cn.ewan.cloud.tenant.data.router.redis.component.MultiRedisFactory;
import cn.ewan.cloud.tenant.data.router.redis.config.MultiRedisConfig;
import cn.ewan.cloud.tenant.data.router.spring.TenantAutoConfiguration;
import cn.ewan.cloud.tenant.data.router.spring.build.BuildUtil;
import cn.ewan.cloud.tenant.data.router.spring.config.TenantConfig;
import com.alibaba.fastjson.JSONObject;
import io.lettuce.core.resource.DefaultClientResources;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;

import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author lgh
 * @date 2023/3/3 10:16
 */

@AutoConfigureBefore({CommonRedisAutoConfiguration.class})
@AutoConfigureAfter({TenantAutoConfiguration.class})
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(value = {MultiRedisConfig.class, TenantConfig.class})
public class TenantRedisAutoConfiguration implements InitializingBean {


    /**
     * @param builderCustomizers 构造器
     * @param multiRedisConfig   redis的配置
     * @param sentinelConfig     哨兵
     * @param clusterConfig      集群
     * @return
     * @throws UnknownHostException
     */
    @Bean
    public MultiRedisFactory connectionFactory(
            TenantConfig tenantConfig,
            MultiRedisConfig multiRedisConfig,
            ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
            ObjectProvider<RedisSentinelConfiguration> sentinelConfig,
            ObjectProvider<RedisClusterConfiguration> clusterConfig
    ) throws UnknownHostException {

        Log.NORMAL.debug("[tenant redis init] multi redis config={}", JSONObject.toJSONString(multiRedisConfig));

        //检查是否有租户未单独配置redis配置
        tenantUserCheck(tenantConfig, multiRedisConfig);

        //构造多工厂数据源
        Map<String, LettuceConnectionFactory> factoryMap = initMultiRedisFactory(multiRedisConfig, builderCustomizers, sentinelConfig, clusterConfig);
        Log.NORMAL.debug("[tenant redis init] multi factory={}", factoryMap);
        return new MultiRedisFactory(factoryMap);
    }


    /**
     * 检查是否有租户未单独配置redis配置
     *
     * @param tenantConfig
     * @param multiRedisConfig
     */
    private void tenantUserCheck(TenantConfig tenantConfig, MultiRedisConfig multiRedisConfig) {
        //查询租户列表
        List<TenantUser> users = tenantConfig.getUsers();
        if (users == null || users.isEmpty()) {
            throw new BusinessException(TenantMsgCode.TENANT_USER_EMPTY);
        }

        //redis配置检查
        Map<String, RedisProperties> multi = multiRedisConfig.getMulti();
        List<TenantUser> notMatchUser = users.stream()
                .filter(x -> multi.get(x.getTenant()) == null).collect(Collectors.toList());
        if (!notMatchUser.isEmpty()) {
            Log.NORMAL.error("tenant user redis config not find, not match tenant-user={}", notMatchUser);
            throw new BusinessException(TenantMsgCode.TENANT_USER_CONFIG_EMPTY);
        }
    }


    /**
     * 初始化多redis数据源工厂
     *
     * @param multiRedisConfig
     * @param builderCustomizers
     * @param sentinelConfig
     * @param clusterConfig
     * @return
     */
    private Map<String, LettuceConnectionFactory> initMultiRedisFactory(MultiRedisConfig multiRedisConfig,
                                                                        ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
                                                                        ObjectProvider<RedisSentinelConfiguration> sentinelConfig,
                                                                        ObjectProvider<RedisClusterConfiguration> clusterConfig) throws UnknownHostException {
        //生成多租户的连接配置
        Map<String, LettuceConnectionFactory> factoryMap = new HashMap<>(10);
        for (Map.Entry<String, RedisProperties> entry : multiRedisConfig.getMulti().entrySet()) {
            LettuceConnectionConfiguration connectionConfig = new LettuceConnectionConfiguration(
                    entry.getValue(),
                    sentinelConfig,
                    clusterConfig
            );
            LettuceConnectionFactory factory = connectionConfig.redisConnectionFactory(builderCustomizers, DefaultClientResources.create());
            factoryMap.put(entry.getKey(), factory);
        }
        return factoryMap;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String buildTime = new BuildUtil().logBuildTime();
        Log.NORMAL.info("===========tenant-redis init finish=======, buildTime={}", buildTime);
    }
}
